package x.ovo.wechat.bot.common.plugin;

import lombok.Cleanup;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import x.ovo.wechat.bot.common.Constant;
import x.ovo.wechat.bot.common.Context;
import x.ovo.wechat.bot.common.command.Command;
import x.ovo.wechat.bot.common.command.CommandExecutor;
import x.ovo.wechat.bot.common.event.Event;
import x.ovo.wechat.bot.common.event.EventListener;
import x.ovo.wechat.bot.common.plugin.config.YamlConfiguration;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;

/**
 * 插件基类
 *
 * @author ovo, created by 2024/05/01
 */
@Slf4j(topic = "插件基类")
@Getter
public abstract class Plugin {

    /** 插件启用状态 */
    private boolean enabled = true;
    /** 描述 */
    @Setter
    private PluginDescription description;
    /** 上下文 */
    @Setter
    private Context context;
    /** 类加载器 */
    @Setter
    private ClassLoader classLoader;
    /** 配置 */
    private YamlConfiguration config;

    /** 可以调用此插件事件监听器的用户 */
    private final Set<String> users = new HashSet<>();

    /**
     * 插件启用时
     */
    public abstract void onEnable();

    /**
     * 插件禁用时
     */
    public abstract void onDisable();

    /**
     * 插件加载时
     */
    public abstract void onLoad();

    /**
     * get 命令
     *
     * @return {@link Command }
     */
    public abstract CommandExecutor getCommandExecutor();

    /**
     * 获取事件侦听器
     *
     * @return {@link EventListener }<{@link ? }>
     */
    public abstract EventListener<? extends Event<?>> getEventListener();

    /**
     * 启用插件
     */
    public void enable() {
        if (enabled) return;
        enabled = true;
        this.onEnable();
        log.info("插件 [{}] 已启用", this.description.getName());
    }

    /**
     * 禁用插件
     */
    public void disable() {
        if (!enabled) return;
        enabled = false;
        this.onDisable();
        log.info("插件 [{}] 已禁用", this.description.getName());
    }

    /**
     * 插件插件中的事件监听器
     */
    public void load() {
        this.onLoad();
        log.info("插件 [{}] 已加载", this.description.getName());
    }

    /**
     * 具有权限
     *
     * @param name 名字
     * @return boolean
     */
    public boolean hasPermissions(String name) {
        return this.users.contains(name);
    }

    /**
     * 获取插件数据文件夹
     *
     * @return {@link File }
     */
    public File getDataFolder() {
        File file = new File(Constant.PLUGIN_DIR, this.description.getName());
        if (!file.exists()) file.mkdirs();
        return file;
    }

    /**
     * 获取插件resources下的文件
     *
     * @param file 文件名
     * @return 文件流
     */
    public InputStream getResource(String file) {
        return getClass().getClassLoader().getResourceAsStream(file);
    }


    /**
     * 保存默认配置
     */
    public void saveDefaultConfig() {
        try {
            @Cleanup InputStream resource = this.getResource("config.yml");
            @Cleanup FileOutputStream fileOutputStream = new FileOutputStream(new File(this.getDataFolder(), "config.yml"));
            byte[] bytes = new byte[resource.available()];
            if (bytes.length == 0) return;
            int length;
            while ((length = resource.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, length);
            }
            fileOutputStream.flush();
        } catch (Exception e) {
            log.error("插件 [{}] 配置文件加载失败", this.description.getName());
        }
    }

    /**
     * 重载配置文件
     */
    public void reloadConfig() {
        this.config = YamlConfiguration.loadConfiguration(new File(this.getDataFolder(), "config.yml"));
    }

    /**
     * 获取配置文件
     */
    public YamlConfiguration getConfig() {
        if (this.config == null) {
            File file = new File(this.getDataFolder(), "config.yml");
            if (file.exists())
                config = YamlConfiguration.loadConfiguration(file);
        }
        return config;
    }

    /**
     * 保存配置
     */
    public void saveConfig() {
        this.config.save(new File(this.getDataFolder(), "config.yml"));
    }

}
